package com.retry.task.admin.biz.service;

import com.google.common.collect.Lists;
import com.retry.task.admin.constants.RetryTaskStatusEnum;
import com.retry.task.admin.dal.mapper.RetryTaskMapper;
import com.retry.task.admin.dal.model.RetryTaskDO;
import com.retry.task.admin.dal.model.query.RetryTaskQuery;
import com.retry.task.admin.utils.CronExpression;
import com.retry.task.admin.utils.NextExecuteTimeUtil;
import com.retry.task.core.constants.RetryTaskExecuteTypeEnum;
import com.retry.task.core.constants.SelectorExectorEnum;
import com.retry.task.core.model.RetryTaskDTO;
import com.retry.task.core.model.base.PageResult;
import com.retry.task.core.model.base.Result;
import com.retry.task.core.utils.CommonExecuteUtils;
import com.retry.task.sequence.Sequence;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author gao.gwq
 * @version 1.0
 * @date 2022/5/3  19:57
 * @Description TODO
 */
@Service
public class RetryTaskBizService {

    private static final Logger LOGGER = LoggerFactory.getLogger(RetryTaskBizService.class);
    @Autowired
    private RetryTaskMapper retryTaskMapper;

    @Autowired
    private Sequence retryTaskSequence;

    public PageResult<List<RetryTaskDTO>> queryRetryTask(RetryTaskQuery query) {

        PageResult<List<RetryTaskDO>> pageResult = queryRetryTaskDO(query);
        if(!pageResult.isSuccess()){
            return PageResult.getFail(pageResult.getCode(),pageResult.getErrMsg());
        }
        PageResult pageResultDTO = PageResult.getSuccess(null);
        pageResultDTO.setTotalCount(pageResult.getTotalCount());

        List<RetryTaskDO> poList = pageResult.getData();
        if(CollectionUtils.isEmpty(poList)){
            return pageResultDTO;
        }
        List<RetryTaskDTO> dtoList = Lists.newArrayList();
        for (RetryTaskDO po : poList) {
            RetryTaskDTO retryTaskDTO = RetryTaskDTO.createBuilder().build();
            BeanUtils.copyProperties(po, retryTaskDTO);
            dtoList.add(retryTaskDTO);
        }
        pageResultDTO.setData(dtoList);
        return pageResultDTO;
    }

    public PageResult<List<RetryTaskDO>> queryRetryTaskDO(RetryTaskQuery query) {
        if (query.getPageSize() > 300) {
            return PageResult.getFail("100", "pageSize  is bigger than 300");
        }

        Map<String, Object> params = CommonExecuteUtils.beanToMap(query, false);
        params.put("offset", (query.getPageNum() - 1) * query.getPageSize());
        params.put("limit", query.getPageSize());
        int count = retryTaskMapper.countByCondition(params);
        PageResult pageResultDTO = PageResult.getSuccess(null);
        pageResultDTO.setTotalCount(count);
        List<RetryTaskDO> poList = retryTaskMapper.queryByCondition(params);
        pageResultDTO.setData(poList);
        return pageResultDTO;
    }

    @Transactional
    public Result<Long> createRetryTask(RetryTaskDTO retryTaskDTO) {
        Result<String> checkResult = checkParams(retryTaskDTO);
        if (!checkResult.isSuccess()) {
            return Result.getFail(checkResult.getCode(), checkResult.getErrMsg());
        }
        if (retryTaskDTO.getIntervalSecond() == null) {
            retryTaskDTO.setIntervalSecond(300);
        }
        if (retryTaskDTO.getMaxExecuteTime() == null) {
            retryTaskDTO.setMaxExecuteTime(1800);
        }
        if (retryTaskDTO.getDelayTime() == null) {
            retryTaskDTO.setDelayTime(300);
        }
        Date firstTime = null;
        try {
            firstTime = NextExecuteTimeUtil.generateFirstTime(retryTaskDTO, new Date());
        } catch (Exception ex) {
            LOGGER.error("retry-task cron expression is not right {}", ex.getMessage(), ex);
            return Result.getFail("100", "cron expression is not right," + ex.getMessage());
        }
        RetryTaskDO retryTaskDO = new RetryTaskDO();
        BeanUtils.copyProperties(retryTaskDTO, retryTaskDO);
        retryTaskDO.setNextPlanTime(firstTime);
        Long id = retryTaskSequence.nextValue();
        retryTaskDO.setId(id);

        retryTaskDO.setOriginRetryNum(retryTaskDO.getRetryNum());
        retryTaskDO.setStatus(RetryTaskStatusEnum.TO_START.getCode());
        retryTaskMapper.insert(retryTaskDO);
        return Result.getSuccess(id);
    }

    public Result<Integer> updateRetryTaskById(RetryTaskDTO retryTaskDTO) {
        if (retryTaskDTO == null) {
            return Result.getFail("100", "retryTaskDTO is null");
        }
        if (retryTaskDTO.getId() == null) {
            return Result.getFail("100", "id is null");
        }
        RetryTaskDO retryTaskDO = new RetryTaskDO();
        BeanUtils.copyProperties(retryTaskDTO, retryTaskDO);
        return Result.getSuccess(retryTaskMapper.updateByPrimaryKey(retryTaskDO));
    }

    private Result<String> checkParams(RetryTaskDTO retryTaskDTO) {

        if (StringUtils.isEmpty(retryTaskDTO.getProjectName())) {
            return Result.getFail("100", "projectName is null");
        }
        if (StringUtils.isEmpty(retryTaskDTO.getTaskName())) {
            return Result.getFail("100", "taskName is null or empty");
        }
        Integer retryNum = retryTaskDTO.getRetryNum();
        if (retryNum == null) {
            retryNum = 1;
            retryTaskDTO.setRetryNum(retryNum);
        }
        if (retryNum > 200) {
            return Result.getFail("100", "retry num  is bigger than 200 not right");
        }
        if (retryNum <= 0) {
            retryTaskDTO.setRetryNum(1);
        }
        Integer executeType = retryTaskDTO.getExecuteType();
        if (executeType == null) {
            return Result.getFail("100", "executeType is null");
        }
        if (executeType.equals(RetryTaskExecuteTypeEnum.CRON.getType())) {
            String cron = retryTaskDTO.getCorn();
            if (StringUtils.isEmpty(cron)) {
                return Result.getFail("100", "cron is null or empty");
            }
            if (CronExpression.isValidExpression(cron) == false) {
                return Result.getFail("100", "cron expression is not right");
            }
        }
        if (executeType.equals(RetryTaskExecuteTypeEnum.INTERVAL.getType())) {
            if (retryTaskDTO.getIntervalSecond() == null) {
                return Result.getFail("100", "interval seconds is null");
            }
            if (retryTaskDTO.getIntervalSecond() < 30) {
                return Result.getFail("100", "interval seconds must be bigger than 30 seconds");
            }
        }
        String selectorType = retryTaskDTO.getSelectorType();
        if (selectorType != null) {
            SelectorExectorEnum selectorExectorEnum = SelectorExectorEnum.getSelectorExector(selectorType);
            if (selectorExectorEnum == null) {
                return Result.getFail("100", "selector type is not right");
            }
        } else {
            retryTaskDTO.setSelectorType(SelectorExectorEnum.LAST_HEART.toString());
        }
        return Result.getSuccess(null);
    }
}
